package com.projektio.io;

public class Image 
{
	private int width;
	private int height;
	private int bpp;
	
	private short[][][] image;
	
	public Image(int width, int height, int bpp)
	{
		this.height = height;
		this.width = width;
		this.bpp = bpp/8; if(this.bpp <= 0) this.bpp = 1;
		this.image = new short[this.height][this.width][this.bpp];
	}
	
	public Image(Image image)
	{
		this.height = image.height;
		this.width = image.width;
		this.bpp = image.bpp;
		this.image = new short[height][width][];
		
		for(int i = 0; i < image.height; i++)
			for(int j = 0; j < image.width; j++)
				this.image[i][j] = image.image[i][j].clone();
	}
	
	public Image getGrayScaleImage()
	{
		Image temp = new Image(getWidth(), getHeight(), 8);
		for(int i = 0; i < height; i++)
		{
			for(int j = 0; j < width; j++)
			{
				short[] color = image[i][j];
				temp.image[i][j] = new short[]{(short)((color[0]*11 + color[1]*16 + color[2]*5)/32)};
			}
		}
		return temp;
	}
	
	public Image getBinaryImage(int threshold)
	{
		if(bpp > 1) throw new IllegalArgumentException("Image must be in grayscale!");
		Image temp = new Image(this);
		for(int i = 0; i < height; i++)
		{
			for(int j = 0; j < width; j++)
			{
				if(image[i][j][0] < threshold) temp.image[i][j] = new short[]{0};
				else temp.image[i][j] = new short[]{255};
			}
		}
		return temp;
	}
	
	public int[] getHistogram(int channel)
	{
		if(channel >= bpp) throw new IllegalArgumentException("Channel doesn't exists!");
		int[] histogram = new int[256];
		
		for(int i = 0; i < height; i++)
			for(int j = 0; j < width; j++)
				histogram[image[i][j][channel]]++;
		
		return histogram;
	}
	
	private float calculateWCV(int[] histogram, int threshold)
	{
		int wMax = 0;
		float wB = 0, wF = 0;
		for(int i = 0; i < histogram.length; i++) wMax += histogram[i];
		for(int i = threshold; i < histogram.length; i++) wF += histogram[i];
		wB = wMax - wF;
		
		float mB = 0;
		float vB = 0;
		
		if(wB != 0)
		{
			for(int i = 0; i < threshold; i++) mB += i*histogram[i];
			mB /= wB;

			for(int i = 0; i < threshold; i++) vB += (Math.pow(i - mB, 2) * histogram[i]);
			vB /= wB;
			
			wB /= wMax;
		}
		
		float mF = 0;
		float vF = 0;
		
		if(wF != 0)
		{
			for(int i = threshold; i < histogram.length; i++) mF += i*histogram[i];
			mF /= wF;
		
			for(int i = threshold; i < histogram.length; i++) vF += (Math.pow(i - mF, 2) * histogram[i]);
			vF /= wF;
			
			wF /= wMax;
		}
		
		return wB * vB + wF * vF;
	}
	
	public int getOtsuThreshold()
	{
		int[] histogram = getHistogram(0);
		int threshold = 0;
		float actualWCV = 0;
		float minWCV = calculateWCV(histogram, 0);
		//System.out.println("WCV[" + 0 + "]: " + minWCV);
		for(int i = 1; i < 255; i++)
		{
			actualWCV = calculateWCV(histogram, i);
			//System.out.println("WCV[" + i + "]: " + actualWCV);
			if(actualWCV < minWCV)
			{
				minWCV = actualWCV;
				threshold = i;
			}
		}
		
		return threshold;
	}
	
	public short[] getPixel(int x, int y)
	{
		return image[y][x];
	}
	
	public void setPixel(int x, int y, short[] color)
	{
		if(color.length == bpp)
		{
			for(int i = 0; i < bpp; i++) color[i] = color[i]<0?0:(color[i]>255)?255:color[i];
			image[y][x] = color;
		}
		else throw new IllegalAccessError("Color depth doesn't match image depth!");
	}
	
	public int getWidth()
	{
		return width;
	}
	
	public int getHeight()
	{
		return height;
	}
	
	public int getBitsPerPixel()
	{
		return bpp*8;
	}
}
